package com.flow.framework.common.util.collection;

import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.util.verify.VerifyUtil;
import com.flow.framework.common.exception.CheckedException;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 容器工具类
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/1/3
 */
@Slf4j
public final class CollectionUtil {

    private static final int ONLY_ONE_ELEMENT = 1;

    /**
     * 获取容器中的第一个元素，如果元素不存在则抛出对象不存在异常
     *
     * @param collection collection
     * @param <T>        T
     * @return
     */
    public static <T> T getFirstElement(Collection<T> collection) {
        if (null == collection) {
            log.error("collection is null.");
            throw new CheckedException(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
        }
        if (collection.isEmpty()) {
            log.error("collection is empty.");
            throw new CheckedException(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
        }
        return collection.iterator().next();
    }


    /**
     * 获取容器中的第一个元素，如果元素不存在则抛异常
     *
     * @param collection        collection
     * @param otherwiseCallback otherwiseCallback
     * @param <T>               T
     * @return
     */
    public static <T, E extends CheckedException> T getFirstElement(Collection<T> collection, Supplier<E> otherwiseCallback) {
        if (null == collection) {
            throw otherwiseCallback.get();
        }
        if (collection.isEmpty()) {
            throw otherwiseCallback.get();
        }
        return collection.iterator().next();
    }

    /**
     * 获取容器中的第一个元素，如果元素不存在则返回默认值
     *
     * @param collection   collection
     * @param defaultValue defaultValue
     * @param <T>          T
     * @return
     */
    public static <T> T getFirstElementQuietly(Collection<T> collection, T defaultValue) {
        if (null == collection) {
            return defaultValue;
        }
        if (collection.isEmpty()) {
            return defaultValue;
        }
        return collection.iterator().next();
    }

    /**
     * 获取容器中的第一个元素，如果元素不存在则返回null
     *
     * @param collection collection
     * @param <T>        T
     * @return
     */
    public static <T> T getFirstElementQuietly(Collection<T> collection) {
        if (null == collection) {
            return null;
        }
        if (collection.isEmpty()) {
            return null;
        }
        return collection.iterator().next();
    }

    /**
     * 将容器转化为指定对象的容器
     *
     * @param collection collection
     * @param converter  converter
     * @param <T>        T
     * @param <E>        E
     * @return
     */
    public static <T, E> List<T> convertToList(Collection<E> collection, Function<E, T> converter) {
        if (null == collection) {
            return Collections.emptyList();
        }
        List<T> tempRecords = new ArrayList<>();
        for (E e : collection) {
            tempRecords.add(converter.apply(e));
        }
        return tempRecords;
    }

    /**
     * 检查容器中元素唯一，如果唯一则获取该元素，否则抛异常
     *
     * @param collection        collection
     * @param otherwiseCallback otherwiseCallback中的参数为传入的容器
     * @param <T>               T为容器中的元素
     * @param <R>               R为需要返回的异常
     * @return
     */
    public static <T, R extends CheckedException> T checkAndGetOnlyOneElement(Collection<T> collection,
                                                                              Function<Collection<T>, R> otherwiseCallback) {
        if (VerifyUtil.isEmpty(collection)) {
            log.error("check only one element error, collection is empty. collection : {}", collection);
            throw otherwiseCallback.apply(collection);
        }
        int size = collection.size();
        if (size != ONLY_ONE_ELEMENT) {
            log.error("check only one element error, element count : {}", size);
            throw otherwiseCallback.apply(collection);
        }
        return collection.iterator().next();
    }
}
